//
// Created by twetec on 25-7-13.
//

#include "handle_tcp_probe_task.h"

#include <sys/socket.h>
#include <arpa/inet.h>

#include <bs_thread_pool/BS_thread_pool.hpp>

using namespace std::chrono_literals;

namespace network_prober::probe
{
// 线程池
static BS::thread_pool s_iopool{1500};

// TCPProbeIPv4::TCPProbeIPv4(const std::string &interface, const std::string &target, const std::uint32_t& port)
//     : m_interface(interface), m_target(target), m_port(port)
// {
//     // 连接的超时时间
//     m_TCPClient.set_connect_timeout(13s);
//
//     // 在socket初始化时调用
//     m_TCPClient.bind_init([this]() {
//         asio::ip::tcp::endpoint endpoint(asio::ip::address_v4::from_string(m_interface), 0); // 0表示任意端口
//         m_TCPClient.socket().bind(endpoint);
//     });
// }

// void TCPProbeIPv4::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//     // 连接成功时调用
//     m_TCPClient.bind_connect([this, self]() {
//         // 回调传出结果
//         m_onProbedCallback(asio2::get_last_error()? -1 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//         // 断开连接，回收资源
//         m_TCPClient.stop();
//
//         // 该代码块执行完， self被释放，当前对象计数器减1.释放当前对象
//     });
// }

// void TCPProbeIPv4::probe()
// {
//     // 建立TCP连接
//     m_TCPClient.async_start(m_target, m_port);
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
// }

// TCPProbeIPv4::~TCPProbeIPv4()
// {
//     m_TCPClient.stop();
//     m_TCPClient.destroy();
// }

HandleTCPProbeIPv4Awaiter::HandleTCPProbeIPv4Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port)
{
}

bool HandleTCPProbeIPv4Awaiter::await_ready()
{
    return false;
}

void HandleTCPProbeIPv4Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 预设延迟为-1
        m_delay = -1;

        // 如果套接字中途失败，则重新申请
        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET, SOCK_STREAM, 0);
            // 创建失败时结束探测
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 重新尝试
                continue;
            }

            // 设置接收超时
            struct timeval tv;
            tv.tv_sec = 6000 / 1000;
            tv.tv_usec = (6000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("超时时间设置失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 设定网口地址
            struct sockaddr_in interfaceAddr;
            memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            interfaceAddr.sin_family = AF_INET;
            interfaceAddr.sin_port = htons(0);
            if (inet_pton(AF_INET, m_interface.c_str(), &interfaceAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("网口设置失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 绑定本机网口
            if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("网口绑定失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 设置目标地址
            struct sockaddr_in targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin_family = AF_INET;
            targetAddr.sin_port = htons(m_port);
            if (inet_pton(AF_INET, m_target.c_str(), &targetAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("目标地址设置失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 请求计时 - 开始时间
            std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

            // 连接服务器
            if (connect(sockfd, (struct sockaddr *)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("服务器连接失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 关闭socket
            close(sockfd);

            // 观测成功，计算延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 输出日志
            SPDLOG_INFO("观测任务执行成功: {} 延迟", m_delay);
            // 结束循环
            break;
        }

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleTCPProbeIPv4Awaiter::await_resume()
{
    return m_delay;
}


// TCPProbeIPv6::TCPProbeIPv6(const std::string &interface, const std::string &target, const std::uint32_t& port)
//     : m_interface(interface), m_target(target), m_port(port)
// {
//     // 连接的超时时间
//     m_TCPClient.set_connect_timeout(13s);
//
//     // 在socket初始化时调用
//     m_TCPClient.bind_init([this]() {
//         asio::ip::tcp::endpoint endpoint(asio::ip::address_v6::from_string(m_interface), 0); // 0表示任意端口
//         m_TCPClient.socket().bind(endpoint);
//     });
// }

// void TCPProbeIPv6::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//     // 连接成功时调用
//     m_TCPClient.bind_connect([this, self]() {
//         // 回调传出结果
//         m_onProbedCallback(asio2::get_last_error()? -1 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//         // 断开连接，回收资源
//         m_TCPClient.stop();
//
//         // 该代码块执行完， self被释放，当前对象计数器减1.释放当前对象
//     });
// }

// void TCPProbeIPv6::probe()
// {
//     // 建立TCP连接
//     m_TCPClient.async_start(m_target, m_port);
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
// }

// TCPProbeIPv6::~TCPProbeIPv6()
// {
//     m_TCPClient.stop();
//     m_TCPClient.destroy();
// }

HandleTCPProbeIPv6Awaiter::HandleTCPProbeIPv6Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port)
{
}

bool HandleTCPProbeIPv6Awaiter::await_ready()
{
    return false;
}

void HandleTCPProbeIPv6Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 预设延迟为-1
        m_delay = -1;

        // 如果套接字中途失败，则重新申请
        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
            // 创建失败时结束探测
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 重新尝试
                continue;
            }

            // 设置接收超时
            struct timeval tv;
            tv.tv_sec = 6000 / 1000;
            tv.tv_usec = (6000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("超时时间设置失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // // 设定网口地址
            // struct sockaddr_in6 interfaceAddr;
            // memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            // interfaceAddr.sin6_family = AF_INET6;
            // interfaceAddr.sin6_port = htons(0);
            // if (inet_pton(AF_INET6, m_interface.c_str(), &interfaceAddr.sin6_addr) <= 0) {
            //     // 关闭socket
            //     close(sockfd);
            //     // 输出日志
            //     SPDLOG_INFO("网口设置失败: {}", strerror(errno));
            //     // 判断错误是否是套接字
            //     if (errno == EBADF) continue;
            //     // 结束执行
            //     break;
            // }
            //
            // // 绑定本机网口
            // if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
            //     // 关闭socket
            //     close(sockfd);
            //     // 输出日志
            //     SPDLOG_INFO("网口绑定失败: {}", strerror(errno));
            //     // 判断错误是否是套接字
            //     if (errno == EBADF) continue;
            //     // 结束执行
            //     break;
            // }

            // 设置目标地址
            struct sockaddr_in6 targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin6_family = AF_INET6;
            targetAddr.sin6_port = htons(m_port);
            if (inet_pton(AF_INET6, m_target.c_str(), &targetAddr.sin6_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("目标地址设置失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 请求计时 - 开始时间
            std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

            // 连接服务器
            if (connect(sockfd, (struct sockaddr *)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("服务器连接失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束执行
                break;
            }

            // 关闭socket
            close(sockfd);

            // 观测成功，计算延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 输出日志
            SPDLOG_INFO("观测任务执行成功: {} 延迟", m_delay);
            // 结束循环
            break;
        }

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleTCPProbeIPv6Awaiter::await_resume()
{
    return m_delay;
}


}